這節會觀察自己定義 initializer。 我們可以使用一個 subClass 來定義一個initializer,前提是這個自定義的 initializer 類別是 callable,也就是類別要實作 call 函式。我們可以沿用上一節的範例,來加入自定義的initializer來觀察。
範例如下:
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras import layers
from tensorflow.keras.models import Model
from tensorflow.keras.datasets import mnist
from tensorflow.keras.initializers import Initializer
import keras.optimizers as optimizers
(train_images, train_labels), (test_images, test_labels) = mnist.load_data()
train_images = train_images.reshape((60000, 28 * 28))
train_images = train_images.astype("float32") / 255
test_images = test_images.reshape((10000, 28 * 28))
test_images = test_images.astype("float32") / 255
class ExampleRandomNormal(Initializer):
def __init__(self, mean, stddev):
self.mean = mean
self.stddev = stddev
def __call__(self, shape, dtype=None):
return tf.random.normal(shape, mean=self.mean, stddev=self.stddev, dtype=dtype)
def get_config(self): # To support serialization
return {'mean': self.mean, 'stddev': self.stddev}
class SimpleDense(layers.Layer):
def __init__(self, units=32):
super(SimpleDense, self).__init__()
self.units = units
def build(self, input_shape):
self.w = self.add_weight(shape=(input_shape[-1], self.units),
initializer= ExampleRandomNormal( mean=0.0 , stddev=0.05 ),
trainable=True)
self.b = self.add_weight(shape=(self.units,),
initializer='random_normal',
trainable=True)
def call(self, inputs):
return tf.matmul(inputs, self.w) + self.b
model = Sequential([
SimpleDense(512),
layers.Dense(10, activation="softmax")
])
model.compile(optimizer=
optimizers.get( {"class_name": "rmsprop", "config": {"learning_rate" : 0.001} } ) ,
loss="sparse_categorical_crossentropy",
metrics=["accuracy"])
model.fit(train_images, train_labels, epochs=1, batch_size=128)
上圖是模型在做build時,針對 initializer 的運作內容做展示。紫色框框就是自定義 initializer 運作過程。自定義initializer類別繼承keras.initializers.Initializer類別。
當使用自定義initializer的Layer正在build時,其中針對initializer設定時會叫用keras.engine.base_layer.Layer.add_weight,進而執行 keras.initializers.get 來設定 Layer 的 initializer屬性。
接下來要準備產生權重張量時,呼叫keras.engine.base_layer_utils.make_variable,針對initializer的使用,這時就會繼續呼叫 ExampleRandomNormal.call。 當keras.engine.base_layer_utils.make_variable產生好權重張量時,回傳給SimpleDense.build函式,供SimpleDense設定權重屬性。接著再繼續執行下一個連接的Layer的build運作。
所以由圖可以觀察到,自定義initializer勢必要將自身的__call__時做出來,這樣才可以讓keras.initializers.get順利偵測出此initializer實體是callable,讓build的運作可以繼續下去。然後這個__call__函式還要跟keras.initializers.Initializer父類別之__call__函式一樣,都要傳入shape。
以上是自定義initializer之運作與要注意的事項。